
package com.lemon.cloud.extension;

import com.lemon.cloud.constsnt.Constant;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * @author lemon
 * @return
 * @description
 * @date 2019-09-15 17:28
 */
@Slf4j
@Component
public class ExtensionPointExecutor extends AbstractExtensionExecutor {

    @Autowired
    private ExtensionPointRepository extensionPointRepository;

    /**
     * @param extensionPointClass
     * @param businessScenario
     * @return Ext
     * @description if the bizScenarioUniqueIdentity is "ali.tmall.supermarket"
     * the search path is as below:
     * 1、first try to get extension by "ali.tmall.supermarket", if get, return it.
     * 2、loop try to get extension by "ali.tmall", if get, return it.
     * 3、loop try to get extension by "ali", if get, return it.
     * 4、if not found, try the default extension
     * @author lemon
     * @date 2019-09-15 17:40
     */
    @Override
    protected <Ext> Ext locateExtensionPoint(Class<Ext> extensionPointClass, BusinessScenario businessScenario) {
        log.debug("Located ExtensionPoint : ", extensionPointClass.getSimpleName());
        checkNull(businessScenario);

        Ext extension;
        String bizScenarioUniqueIdentity = businessScenario.getUniqueIdentity();
        log.debug("BizScenario in locateExtension is : " + bizScenarioUniqueIdentity);

        // first try
        extension = firstTry(extensionPointClass, bizScenarioUniqueIdentity);

        if (extension != null) {
            return extension;
        }

        // loop try
        extension = loopTry(extensionPointClass, bizScenarioUniqueIdentity);

        if (extension != null) {
            return extension;
        }

        throw new RuntimeException("Can not find extension with ExtensionPoint: " + extensionPointClass + ", BusinessScenario:" + bizScenarioUniqueIdentity);
    }

    /**
     * @param extensionPointClass
     * @param bizScenarioUniqueIdentity
     * @return Ext
     * @description
     * @author lemon
     * @date 2019-09-15 17:40
     */
    private <Ext> Ext firstTry(Class<Ext> extensionPointClass, String bizScenarioUniqueIdentity) {
        return (Ext) extensionPointRepository.getExtensionPoint(new ExtensionCoordinate(extensionPointClass.getName(), bizScenarioUniqueIdentity));
    }

    /**
     * @param extensionPointClass
     * @param bizScenarioUniqueIdentity
     * @return Ext
     * @description
     * @author lemon
     * @date 2019-09-15 17:40
     */
    private <Ext> Ext loopTry(Class<Ext> extensionPointClass, String bizScenarioUniqueIdentity) {
        Ext extension;

        if (bizScenarioUniqueIdentity == null) {
            return null;
        }

        int lastDotIndex = bizScenarioUniqueIdentity.lastIndexOf(Constant.DOT_SEPARATOR);

        while (lastDotIndex != -1) {
            bizScenarioUniqueIdentity = bizScenarioUniqueIdentity.substring(0, lastDotIndex);
            extension = (Ext) extensionPointRepository.getExtensionPoint(new ExtensionCoordinate(extensionPointClass.getName(), bizScenarioUniqueIdentity));

            if (extension != null) {
                return extension;
            }

            lastDotIndex = bizScenarioUniqueIdentity.lastIndexOf(Constant.DOT_SEPARATOR);
        }
        return null;
    }

    /**
     * @param businessScenario
     * @return void
     * @description
     * @author lemon
     * @date 2019-09-15 17:40
     */
    private void checkNull(BusinessScenario businessScenario) {
        if (businessScenario == null) {
            throw new RuntimeException("BusinessScenario can not be null for extension");
        }
    }
}
